# $Id$
$: << File.dirname(__FILE__)

require 'rubygems'
begin
  gem 'soap4r'
rescue
  require_gem 'soap4r'
end
require 'soap/soap'
require 'soap/mapping'
require "wss4r/rpc/wssdriver"
require "wss4r/tokenresolver/certificateresolver"
require 'defaultDriver'
require 'aws_credentials'
require 'rexml_extensions'
 
class AmazonFPS
  include SOAP::RPC
  include WSS4R::Tokenresolver
  
  VERSION = '1.0.0'
  SANDBOX_ENDPOINT = 'https://fps.sandbox.amazonaws.com'
  LIVE_ENDPOINT    = 'https://fps.amazonaws.com'
  CERTIFICATE_DIR  = File.join(File.dirname(__FILE__), '..', 'certs')
  
  # Initializes the connection to AmazonFPS and sets up cryptographic
  # signature features
  #
  # Credentials hash should include:
  # AWSAccessKeyId :: Your AWS Access Key
  #
  # Opts hash can include:
  # :certificate_dir :: Directory in which certificates live
  # :subject :: Subject for x509 certificate
  def initialize(credentials, opts={}, sandbox=false)
    @options = opts
    @endpoint_url = sandbox ? SANDBOX_ENDPOINT : LIVE_ENDPOINT
    @certificate_dir = @options[:certificate_dir] ? @options[:certificate_dir] : CERTIFICATE_DIR
    @aws = AmazonFPSPortType.new(@endpoint_url)
    @aws.wiredump_dev = STDERR if $DEBUG
    @aws.options["protocol.http.ssl_config.verify_mode"] = nil
    sign()
    @credentials = Credentials.new(credentials)
    @credentials.handlers.each do |h|
      @aws.headerhandler << h
    end
  end
  
  private 
  
  def sign
    @resolver = CertificateDirectoryResolver.new(@certificate_dir)
    @aws.security().clear_resolver!()
    @aws.security().add_security_resolver(@resolver)
    sign_cert = @resolver.certificate_by_subject(@options[:subject])
    pkey = @resolver.private_key(sign_cert)
    x509 = X509SecurityToken.new(sign_cert, pkey)
    signature = Signature.new(x509)
    @aws.security().add_security_token(signature)
 	end
  
  def method_missing(method, *args)
    begin
      res = eval "@aws.#{method}(*args)"
      raise AWSError.new(method, res.errors) if res.respond_to?(:errors) && ! res.errors.nil?
    rescue SOAP::FaultError => fault
      msg = "#{method} Call Failed: [#{fault.faultcode.to_s}]#{fault.faultstring.to_s}"
      $stderr.puts(msg)
    end
    
    res
  end
  
  class AWSError < StandardError
    def initialize(method, errors)
      @message = String.new
      errors.each do |e|
        msg = sprintf("Error in AmazonFPS::%s -- [%s] %s\n", method, e.errorCode, e.reasonText)
        @message += msg
        $stderr.puts(msg)
      end
    end
    
    def to_s
      @message
    end
  end
end

